using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using 数据挖掘实验.Logger;

namespace 数据挖掘实验
{
    public class ConfidenceItem: IEnumerable<ConfidenceItem>
    {
        ConfidenceItem prior; // 前一个节点
        Dictionary<int, ConfidenceItem> nextDic = new Dictionary<int, ConfidenceItem>(); // 下一个节点
        List<int> itemList;
        List<int> conditionList = null; 
        public int Key { get; }
        public float Confidence { get; private set; }

        /// <summary>
        /// 获取前提项集
        /// </summary>
        public List<int> ConditionList 
        {
            get
            {
                if (conditionList == null)
                {
                    var aimIndexStack = GetIndexStack(); // Y - X'
                    List<int> conditionIndexList = itemList.Where(x => !aimIndexStack.Contains(x)).ToList();
                    conditionList = conditionIndexList;
                }
                return conditionList;
            }
        }

        public ConfidenceItem(int key)
        {
            Key = key;
        }

        /// <summary>
        /// 开始计算置信度
        /// </summary>
        /// <param name="startPos">起始点</param>
        /// <param name="collector">Apriori采集器</param>
        /// <param name="originCount">条件结论组合计数</param>
        /// <param name="itemList">从AprioriController到该AprioriItem节点的所有下标</param>
        /// <param name="transaction">事务</param>
        /// <param name="confidenceThreshold">置信度阈值(最小置信度)</param>
        /// <returns>是否满足最小置信度</returns>
        public bool Compute(int startPos, AprioriCollector collector, int originCount, List<int> itemList,
            List<string> transaction,
            float confidenceThreshold)
        {
            if (startPos > itemList.Count) // 是否已经是最后一个项
                return false;

            if (GetIndexStack().Count == itemList.Count) // 所有项都是条件没有结论是不允许的
                return false;

            this.itemList = itemList; // 存储所属AprioriItem的项集
            var item = collector[ConditionList];
            
            int count = item.Count; // σ(X')
            Confidence = (float) originCount / count; // σ(Y)/σ(X')

            LogManager.AprioriLogger.Log(GetFormulaString(transaction));
            if (Confidence < confidenceThreshold)
                return false;

            for (int i = startPos; i < itemList.Count; i++)
            {
                var key = itemList[i];
                var next = new ConfidenceItem(key) {prior = this};
                if (next.Compute(i + 1, collector, originCount, itemList,transaction, confidenceThreshold))
                    nextDic.Add(key, next);
                else
                    next.prior = null;
            }
            return true;
        }

        /// <summary>
        /// 获取索引栈
        /// </summary>
        /// <returns>索引栈</returns>
        public Stack<int> GetIndexStack()
        {
            var stack = new Stack<int>();
            ConfidenceItem item = this;
            do
            {
                stack.Push(item.Key);
            } while ((item = item.prior) != null);

            return stack;
        }
        

        public bool TryGetPrior(out ConfidenceItem priorItem)
        {
            if (prior == null)
            {
                priorItem = null;
                return false;
            }
            priorItem = prior;
            return true;
        }

        public string GetFormulaString(List<string> formList)
        {
            Stack<int> aimStack = GetIndexStack();
            var strCondition = conditionList.Select(index => formList[index]);
            var strAim = aimStack.Select(index => formList[index]);

            return $"Confidence({string.Join(",", strCondition)}=>{string.Join(",", strAim)})={Confidence}";
        }

        public IEnumerator<ConfidenceItem> GetEnumerator()
        {
            var childrenQueue = new Queue<Dictionary<int, ConfidenceItem>>();
            childrenQueue.Enqueue(nextDic);
            while (childrenQueue.TryDequeue(out var children))
            {
                foreach (var child in children.Values)
                {
                    childrenQueue.Enqueue(child.nextDic);
                    yield return child;
                }
            }
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        public string GetItemsStr(List<string> formList)
        {
            Stack<int> aimStack = GetIndexStack();
            var strCondition = conditionList.Select(index => formList[index]);
            var strAim = aimStack.Select(index => formList[index]);

            return $"{string.Join(",", strCondition)}=>{string.Join(",", strAim)}";
        }
    }
}